/**
 *    Copyright (C) 2008, 2009  Victor Smirnov <aist11@gmail.com>
 *
 *    This program is free software; you can redistribute it and/or modify
 *    it under the terms of the GNU General Public License as published by
 *    the Free Software Foundation; either version 3 of the License, or
 *    (at your option) any later version.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU General Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License along
 *    with this program; if not, write to the Free Software Foundation, Inc.,
 *    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 *    See LICENSE.exceptions in the root folder of this project for possible
 *    exceptions
 *
 */


package ru.teleology.cubefs.api.reflection;

import java.util.HashMap;
import java.util.Map;

import ru.teleology.cubefs.api.models.AbstractJavaLogHandler;
import ru.teleology.cubefs.api.models.Container;
import ru.teleology.cubefs.api.models.DefaultLogHandler;
import ru.teleology.cubefs.api.models.IdxMapIterator;
import ru.teleology.cubefs.api.models.InputStreamAdaptor;
import ru.teleology.cubefs.api.models.Iterator;
import ru.teleology.cubefs.api.models.Logger;
import ru.teleology.cubefs.api.models.Manager;
import ru.teleology.cubefs.api.models.Model;
import ru.teleology.cubefs.api.models.OutputStreamAdaptor;
import ru.teleology.cubefs.api.models.Page;
import ru.teleology.cubefs.api.models.StreamManager;
import ru.teleology.cubefs.api.models.TreeMapIterator;
import ru.teleology.cubefs.api.models.Vector;
import ru.teleology.cubefs.api.reflection.list.List;
import ru.teleology.cubefs.api.reflection.list.ListOfBigInt;
import ru.teleology.cubefs.api.reflection.list.ListOfStrings;
import ru.teleology.cubefs.api.reflection.list.ListOfTyped;
import ru.teleology.cubefs.api.reflection.list.ListOfTypes;
import ru.teleology.cubefs.api.reflection.list.StaticListOfTyped;
import ru.teleology.cubefs.api.reflection.list.array.ByteArray;
import ru.teleology.cubefs.api.reflection.metadata.BigIntField;
import ru.teleology.cubefs.api.reflection.metadata.BitmapField;
import ru.teleology.cubefs.api.reflection.metadata.ByteField;
import ru.teleology.cubefs.api.reflection.metadata.ContainerMetadata;
import ru.teleology.cubefs.api.reflection.metadata.FieldMetadata;
import ru.teleology.cubefs.api.reflection.metadata.FlagField;
import ru.teleology.cubefs.api.reflection.metadata.IDField;
import ru.teleology.cubefs.api.reflection.metadata.IntField;
import ru.teleology.cubefs.api.reflection.metadata.Metadata;
import ru.teleology.cubefs.api.reflection.metadata.MetadataGroup;
import ru.teleology.cubefs.api.reflection.metadata.ModelMetadata;
import ru.teleology.cubefs.api.reflection.metadata.NumberField;
import ru.teleology.cubefs.api.reflection.metadata.PageMetadata;
import ru.teleology.cubefs.api.reflection.metadata.ShortField;
import ru.teleology.cubefs.api.reflection.metadata.UByteField;
import ru.teleology.cubefs.api.reflection.metadata.UShortField;
import ru.teleology.cubefs.api.reflection.number.BigIntNumber;
import ru.teleology.cubefs.api.reflection.number.ByteNumber;
import ru.teleology.cubefs.api.reflection.number.DoubleNumber;
import ru.teleology.cubefs.api.reflection.number.FloatNumber;
import ru.teleology.cubefs.api.reflection.number.IntNumber;
import ru.teleology.cubefs.api.reflection.number.Number;
import ru.teleology.cubefs.api.reflection.number.ShortNumber;
import ru.teleology.cubefs.api.reflection.number.UShortNumber;
import ru.teleology.cubefs.api.reflection.value.BooleanValue;
import ru.teleology.cubefs.api.reflection.value.ConstStringValue;
import ru.teleology.cubefs.api.reflection.value.ConstVoidPtrValue;
import ru.teleology.cubefs.api.reflection.value.StringRefValue;
import ru.teleology.cubefs.api.reflection.value.StringValue;
import ru.teleology.cubefs.api.reflection.value.TypedIDValue;
import ru.teleology.cubefs.api.reflection.value.Value;
import ru.teleology.cubefs.api.reflection.value.VersionValue;
import ru.teleology.cubefs.api.reflection.value.VoidPtrValue;

@NativeTypes(types = {
		Type.class, 
		Typed.class, 
		Operation.class, 
		Parameter.class, 
		
		List.class,
		ListOfTyped.class,
		StaticListOfTyped.class,
		ListOfTypes.class,
		ListOfStrings.class,
		ListOfBigInt.class,
		
		CoreFactory.class,
		
		ByteArray.class,
		
		Number.class,
		ByteNumber.class,
		ShortNumber.class,
		UShortNumber.class,
		IntNumber.class,
		BigIntNumber.class,
		DoubleNumber.class,
		FloatNumber.class,
		
		Value.class,
		BooleanValue.class,
		TypedIDValue.class,
		VersionValue.class,
		ConstStringValue.class,
		StringRefValue.class,
		StringValue.class,
		VoidPtrValue.class,
		ConstVoidPtrValue.class,
		
		ContainerMetadata.class,
		FieldMetadata.class,
		NumberField.class,
		BitmapField.class,
		IDField.class,
		FlagField.class,
		Metadata.class,
		MetadataGroup.class,
		ModelMetadata.class,
		PageMetadata.class,
		ByteField.class,
		UByteField.class,
		ShortField.class,
		UShortField.class,
		IntField.class,
		BigIntField.class,
		
		
		Page.class,
		Manager.class,
		StreamManager.class,
		Model.class,
		Container.class,
		Iterator.class,
		TreeMapIterator.class,
		IdxMapIterator.class,
		ru.teleology.cubefs.api.models.Map.class,
		ru.teleology.cubefs.api.models.Collection.class,
		ru.teleology.cubefs.api.models.Map.Pair.class,
		ru.teleology.cubefs.api.models.TreeMap.class,
		ru.teleology.cubefs.api.models.IdxMap.class,
		ru.teleology.cubefs.api.models.blobmap.Data.class,
		ru.teleology.cubefs.api.models.blobmap.DataIterator.class,
		ru.teleology.cubefs.api.models.blobmap.DynVector.class,
		ru.teleology.cubefs.api.models.blobmap.BlobMap.class,
		ru.teleology.cubefs.api.models.blobmap.BlobMapIterator.class,
		Vector.class,
		InputStreamAdaptor.class,
		OutputStreamAdaptor.class,
		DefaultLogHandler.class,
		AbstractJavaLogHandler.class,
		Logger.class
})
public class TypeSystem {
	
	private static boolean initialized = false;
	private static final Map<String, Class<Typed>> mapping = new HashMap<String, Class<Typed>>();
	
	static {
		InitTypes();
	}
	
	public static void InitTypes() {
		if (!initialized) {
//			String apiname = "/home/victor/workspace1/core/build/api-impl/posix/libcubefs-core-api.so";
//			LibraryLoader.load(apiname, LibraryLoader.GLOBAL);
//			System.load(apiname);
			
			
			String name = "/home/victor/workspace1/core-java-native/build/native/libcubefs-core-java.x86_64.so";
			System.load(name);
			LibraryLoader.load(name, LibraryLoader.GLOBAL);
			
			String[] env = getSystemEnvironment();
			InitTypeSystem(env);
			InitStreams(InputStreamAdaptor.class, OutputStreamAdaptor.class);
			InitLogHandler(AbstractJavaLogHandler.class);
			
			BuildMapping();
			
			Types.init();
			Typed.initNotifier();
			
			for (Type type : list()) {
				type.buildProxies();
			}
			
			initialized = true;
		}
	}
	
	private static String[] getSystemEnvironment() {
		Map<String, String> env = System.getenv();
		
		String[] envs = new String[env.size()];
		
		int idx = 0;
		for (Map.Entry<String, String> entry : env.entrySet()) 
		{
			envs[idx++] = entry.getKey()+"="+entry.getValue();
		}
		
		return envs;
	}

	private static void BuildMapping() {
		NativeTypes types = TypeSystem.class.getAnnotation(NativeTypes.class);
		for (Class<Typed> type: types.types()) {
			NativeType ntype = type.getAnnotation(NativeType.class);
			if (ntype != null) {
				mapping.put(ntype.name(), type);
			}
			else {
				System.out.println("Class "+type+" has no annotation");
			}
		}
	}

	public static void DestroyTypes() {
		if (initialized) {
			DestroyTypeSystem();
			CleanStreams();
			mapping.clear();
			initialized = false;
		}
	}
	
	public static Type find(String name) {
		long handle = FindType(name);
		if (handle != 0) {
			return (Type) Types.getInstance(handle, Type.class);
		}
		else {
			throw new RuntimeException("Native type "+name+" is not found in the type registry");
		}
	}
	
	public static Type find(Class<?> clazz) {
		NativeType ntype = clazz.getAnnotation(NativeType.class);
		if (ntype != null) {
			return find(ntype.name());
		}
		else {
			throw new RuntimeException("NativeType annotaition is not defined for "+clazz);
		} 
	}
	
	public static ListOfTypes list() {
		return Types.getInstance(ListTypes(), ListOfTypes.class);
	}
	
	private static native void InitTypeSystem(String[] env);
	private static native void DestroyTypeSystem();
	private static native long FindType(String name);	
	private static native long ListTypes();
	
	private static native void InitStreams(Class<?> inClass, Class<?> outClass);
	private static native void InitLogHandler(Class<?> handlerClass);
	private static native void CleanStreams();
	
	public static Class<?> findJavaClass(Type type) {
		Class<?> clazz = mapping.get(type.name());
		if (clazz != null) {
			return clazz;
		}
		else if (type == Types.VoidType()) {
			return void.class;
		}
		else {
			Type parent = type.parent();
			if (parent != null) {
				return findJavaClass(parent);
			}
			else {
				return null;
			}
		}
	}

	public static Map<String, Class<Typed>> getMapping() {
		return mapping;
	}
}
